#include "Bucket.h"

Bucket::Bucket(char* block, int blockSize) {
	// Tomo el tamaño de la dispersion
	this->buildBucketFromBlock(block, blockSize);
}

Bucket::Bucket(int ds, int blockSize) {
	// Tomo el tamaño de la dispersion
	this->dispersionSize = ds;
	//Tomo la cantidad de Bytes libres del Bucket
	this->bucketSize = blockSize;
	this->freeBytes = blockSize - (sizeof(int) * 2);
	this->records = new vector<Record*> ;
}

vector<Record*>* Bucket::getRecordsFromBlock(char* recordBlock) {
	int bytes = this->bucketSize - (sizeof(int) * 2);
	char* currentAddress = recordBlock;
	vector<Record*>* records = new vector<Record*> ;
	while (this->getFreeBytes() != bytes) {
		int* lengthRecord = (int*) currentAddress;
		char* initialAddress = currentAddress + sizeof(int);
		Record* newRecord = new Record(*lengthRecord, initialAddress);
		records->push_back(newRecord);
		bytes = bytes - *lengthRecord - (sizeof(int));
		currentAddress = currentAddress + *lengthRecord + (sizeof(int));
	}
	return records;

}
void Bucket::buildBucketFromBlock(char * block, int bucketSize) {
	int* ds = (int*) block;
	this->dispersionSize = *ds;
	int* freeBytes = (int*) (block + sizeof(int));
	this->bucketSize = bucketSize;
	this->freeBytes = *freeBytes;
	this->records = this->getRecordsFromBlock(block + (sizeof(int) * 2));
}

//estados Bucket::insertRecord(Record* newRecord) {
//	//bytes record and metadata record
////	int position = this->searchRecord(newrecord->getKey());
//	/*
//	 * INSERTAR SOLO UN OFFSET Y NADA MAS
//	// */
//	int bytesNewRecord = newRecord->getBytes() + sizeof(int);
//	if ((this->getBytes()+bytesNewRecord) > this->getMaxCharge() ){
//		return full;
//	}else{
//		this->getRecords()->push_back(*newRecord);
//		return ok;
//	}
//}
estados Bucket::insertRecord(Record* newRecord) {
	//bytes record and metadata record
	//	int position = this->searchRecord(newrecord->getKey());
	/*
	 * INSERTAR SOLO UN OFFSET Y NADA MAS
	 // */
	int recordNumber = this->searchRecord(newRecord->getKey());
	if (recordNumber == -1) {
		int bytesNewRecord = newRecord->getBytes() + sizeof(int);
		if ((this->getBytes() + bytesNewRecord) > this->getMaxCharge()) {
			return full;
		} else {
			this->getRecords()->push_back(newRecord);
			this->freeBytes = this->freeBytes - sizeof(int) - (newRecord)->getBytes();
			return ok;
		}

	} else {
		// El registro ya existia en el bucket, se lo modifica, verifico que no quede en overflow
		int space = 0;
		int offset = 0;
		for (unsigned int i = 0; i < newRecord->getData()->size(); i++) {
			offset = newRecord->getData()->at(i);
			if ((this->records->at(recordNumber)->searchOffset(offset)) == -1) {
				this->records->at(recordNumber)->insertOffset(offset);
				space += sizeof(int);
			};
		}
		//si no queda espacio libre, se lleno el bucket, no puedo insertar !
		if ((this->getFreeBytes() - space) < 0) {
			newRecord->setData(this->getRecords()->at(recordNumber)->getData()); //devuelvo el registro entero en el q querian agregar
			this->getRecords()->erase(this->records->begin() + recordNumber); //todo VER!
			return full;

		} else {
			//actualizar FREE BYTES!
			this->freeBytes = this->freeBytes - space;
			return ok;
		}
	}

}

int Bucket::searchRecord(string key) {
	unsigned int i = 0;

	while (i < this->records->size()) {
		if (this->records->at(i)->getKey() == key) {
			return i;
		}
		i++;
	}
	return -1;
}

estados Bucket::deleteRecord(Record* record) {
	int position = this->searchRecord(record->getKey());
	if (position != -1) {
		int positionOffset = this->records->at(position)->searchOffset(record->getData()->at(0));
		if (positionOffset != -1) {
			this->records->at(position)->getData()->erase((this->records->at(position)->getData()->begin()) + positionOffset);
			if (this->records->at(position)->getData()->size() == 0) {
				this->freeBytes = this->freeBytes + record->getBytes() + sizeof(int);
				this->records->erase((this->records->begin()) + (position));
			} else {
				this->freeBytes = this->freeBytes + sizeof(int);
			}
			if (this->records->size() > 0) {
				return ok;
			} else {
				return empty;
			}
		} else {
			return noaction;
		}
	} else {
		return noaction;
	}
}
//estados Bucket::deleteRecord(Record* record) {
//	int position = this->searchRecord(record->getKey());
//	if (position != -1) {
//		this->freeBytes = this->freeBytes + record->getBytes() + sizeof(int);
//		this->records->erase((this->records->begin()) + (position)); //borrarlo del vectooor!!
//
//		if (this->records->size() > 0) {
//			return ok;
//		} else {
//			return empty;
//		}
//	} else {
//		return noaction;
//	}
//}
#include <iostream>

char* Bucket::getBlock() {

	//  creo el buffer donde voy a guardar todo
	//char* buffer = new char[this->getBytes()];
	char* buffer = new char[this->getBucketSize()];
	int bufferPosition = 0;
	//   guardo el tama�o de la dispersion
	*(int*) (buffer + bufferPosition) = this->getDispersionSize();
	bufferPosition += sizeof(int);
	// guardo los bytes libres
	*(int*) (buffer + bufferPosition) = this->getFreeBytes();
	bufferPosition += sizeof(int);
	//guardo elemento por elemento del vector
	int cant = this->getRecords()->size();

	for (int i = 0; i < cant; i++) {
		//guardo el tama�o de cada registro del vector
		*(int*) (buffer + bufferPosition) = this->getRecords()->at(i)->getBytes();
		bufferPosition += sizeof(int);

		char* recordBlock = this->getRecords()->at(i)->BuildRecordStream();
		for (int j = 0; j < (this->getRecords()->at(i)->getBytes()); j++) {
			*(buffer + bufferPosition + j) = recordBlock[j];
		}

		bufferPosition += this->getRecords()->at(i)->getBytes();
	}

	//bufferPosition += (this->getData()->size())*sizeof(int);

	return buffer;

}

int Bucket::getDispersionSize() {
	return this->dispersionSize;

}

int Bucket::getBytes() {
	int bytes = 0;
	for (unsigned int i = 0; i < this->getRecords()->size(); i++) {
		//record bytes
		bytes = this->getRecords()->at(i)->getBytes() + bytes;
		//record metda data
		bytes = sizeof(int) + bytes;
	}
	//meteda bucket
	return bytes + (sizeof(int) * 2);
}

vector<Record*> *Bucket::getRecords() {
	return this->records;
}

int Bucket::getBucketSize() const {
	return bucketSize;
}

int Bucket::getFreeBytes() {

	return this->freeBytes;
}

int Bucket::getMaxCharge() const {
	return ((this->getBucketSize() * 80) / 100);
}

void Bucket::setBucketSize(int bucketSize) {
	this->bucketSize = bucketSize;
}

void Bucket::setDispersionSize(int dispersionSize) {
	this->dispersionSize = dispersionSize;
}

void Bucket::setRecords(vector<Record*> *records) {
	this->records = records;
}

Bucket::~Bucket() {
	records->clear();
	delete records;
}
